package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Fxz
 * @version 1.0
 * @date 2022-02-19 21:33
 */
@SuppressWarnings("all")
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderService {

	@Value("${ware.url}")
	private String wareUrl;

	private final RabbitService rabbitService;

	private final RedisTemplate redisTemplate;

	private final OrderInfoMapper orderInfoMapper;

	private final OrderDetailMapper orderDetailMapper;

	/**
	 * 保存订单
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public Long saveOrderInfo(OrderInfo orderInfo) {
		// 计算总金额
		orderInfo.sumTotalAmount();
		// 订单状态
		orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
		// 生成订单交易编号
		String outTradeNo = UUID.randomUUID().toString() + System.currentTimeMillis();
		orderInfo.setOutTradeNo(outTradeNo);
		orderInfo.setCreateTime(new Date());
		// 日历
		Calendar calendar = Calendar.getInstance();
		// 增加一天
		calendar.add(Calendar.DATE, 1);
		// 默认一天的过期时间
		orderInfo.setExpireTime(calendar.getTime());
		// 设置进程状态
		orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
		orderInfoMapper.insert(orderInfo);

		List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
		if (CollectionUtils.isNotEmpty(orderDetailList)) {
			orderDetailList.forEach(item -> {
				item.setOrderId(orderInfo.getId());
				orderDetailMapper.insert(item);
			});
		}

		// 发送延时消息
		rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL,
				orderInfo.getId(), MqConst.DELAY_TIME);
		log.info("发送订单延时消息...");

		return orderInfo.getId();
	}

	/**
	 * 获取流水号
	 */
	@Override
	public String getTradeNo(String userId) {
		String uuId = UUID.randomUUID().toString();
		String tradNoKey = "user:" + userId + ":tradeCode";
		redisTemplate.opsForValue().set(tradNoKey, uuId);
		return uuId;
	}

	/**
	 * 比较流水号
	 */
	@Override
	public Boolean checkTradeNo(String tradeNo, String userId) {
		String tradNoKey = "user:" + userId + ":tradeCode";
		String no = (String) redisTemplate.opsForValue().get(tradNoKey);
		return tradeNo.equals(no);
	}

	/**
	 * 删除流水号
	 */
	@Override
	public void deleteTradeNo(String userId) {
		String tradNoKey = "user:" + userId + ":tradeCode";
		redisTemplate.delete(tradNoKey);
	}

	/**
	 * 验证库存
	 */
	@Override
	public Boolean checkStock(Long skuId, Integer skuNum) {
		String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
		return "1".equals(result);
	}

	/**
	 * 修改订单状态
	 */
	@Override
	public void execExpiredOrder(Long orderId) {
		log.info("更新订单状态:{}", orderId);

		updateOrderStatus(orderId, ProcessStatus.CLOSED);

		// 发送一个消息关闭交易记录
		rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);
	}

	/**
	 * 跟据订单id更新订单状态
	 */
	@Override
	public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setId(orderId);
		orderInfo.setProcessStatus(processStatus.name());
		orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
		orderInfoMapper.updateById(orderInfo);
	}

	/**
	 * 跟据订单id 查询订单信息
	 */
	@Override
	public OrderInfo getOrderInfo(Long orderId) {
		OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
		List<OrderDetail> orderDetails = orderDetailMapper
				.selectList(Wrappers.<OrderDetail>lambdaQuery().eq(OrderDetail::getOrderId, orderId));
		Optional.ofNullable(orderInfo).ifPresent(info -> info.setOrderDetailList(orderDetails));
		return orderInfo;
	}

	/**
	 * 跟据orderId发送消息给库存并修改订单状态
	 */
	@Override
	public void sendOrderStatus(Long orderId) {
		// 更改订单状态
		updateOrderStatus(orderId, ProcessStatus.NOTIFIED_WARE);
		// 发送消息
		String wareJson = initWareOrder(orderId);
		rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, wareJson);
	}

	/**
	 * 发送减库存消息的字符串
	 */
	@Override
	public String initWareOrder(Long orderId) {
		OrderInfo orderInfo = getOrderInfo(orderId);
		Map map = initWareOrder(orderInfo);
		return JSON.toJSONString(map);
	}

	/**
	 * 将orderInfo的部分字段转换为map集合`
	 */
	@Override
	public Map initWareOrder(OrderInfo orderInfo) {
		HashMap<String, Object> map = new HashMap<>();
		map.put("orderId", orderInfo.getId());
		map.put("consignee", orderInfo.getConsignee());
		map.put("consigneeTel", orderInfo.getConsigneeTel());
		map.put("orderComment", orderInfo.getOrderComment());
		map.put("orderBody", orderInfo.getTradeBody());
		map.put("deliveryAddress", orderInfo.getDeliveryAddress());
		map.put("paymentWay", "2");
		// 仓库Id ，减库存拆单时需要使用！
		map.put("wareId", orderInfo.getWareId());
		ArrayList<Map> mapArrayList = new ArrayList<>();
		List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
		orderDetailList.forEach(orderDetail -> {
			HashMap<String, Object> orderDetailMap = new HashMap<>();
			orderDetailMap.put("skuId", orderDetail.getSkuId());
			orderDetailMap.put("skuNum", orderDetail.getSkuNum());
			orderDetailMap.put("skuName", orderDetail.getSkuName());
			mapArrayList.add(orderDetailMap);
		});
		map.put("details", mapArrayList);
		return map;
	}

	/**
	 * 拆单方法 获取所有子订单集合
	 */
	@Transactional
	@Override
	public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
		// 子订单集合
		ArrayList<OrderInfo> subOrderInfoList = new ArrayList<>();
		// 获取原始订单
		OrderInfo orderInfoOrigin = getOrderInfo(Long.parseLong(orderId));
		// 转换
		List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
		if (CollectionUtils.isNotEmpty(mapList)) {
			mapList.forEach(map -> {
				// 获取仓库id
				String wareId = (String) map.get("wareId");
				// 获取skuId列表
				List<String> skuIds = (List<String>) map.get("skuIds");
				// 创建新的子订单
				OrderInfo subOrderInfo = new OrderInfo();
				// 属性赋值
				BeanUtils.copyProperties(orderInfoOrigin, subOrderInfo);
				// 父订单id
				subOrderInfo.setParentOrderId(Long.parseLong(orderId));
				// id置为null 令其自增
				subOrderInfo.setId(null);
				// 赋值仓库id
				subOrderInfo.setWareId(wareId);
				// 计算总金额(需要订单明细)
				List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
				// 在原始订单中过滤出子订单的订单明细
				List<OrderDetail> detailList = orderDetailList.stream()
						.filter(item -> skuIds.contains(String.valueOf(item.getSkuId()))).collect(Collectors.toList());
				// 为子订单赋值订单明细
				subOrderInfo.setOrderDetailList(detailList);
				subOrderInfo.sumTotalAmount();

				// 保存子订单
				saveOrderInfo(subOrderInfo);
				subOrderInfoList.add(subOrderInfo);
			});
		}

		// 修改原始订单状态
		updateOrderStatus(Long.parseLong(orderId), ProcessStatus.SPLIT);

		return subOrderInfoList;
	}

	/**
	 * 关闭过期订单
	 * @param orderId 订单id
	 * @param flag 是否需要关闭paymentInfo
	 */
	@Override
	public void execExpiredOrder(Long orderId, String flag) {
		log.info("更新订单状态:{}", orderId);

		updateOrderStatus(orderId, ProcessStatus.CLOSED);

		if ("2".equals(flag)) {
			// 发送一个消息关闭交易记录
			rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);
		}
	}

}
